package control

import (
	"jvm/instructions/base"
	"jvm/rtda"
	. "jvm/typedef"
)

// *****************************************************************
// 无条件跳转 跳转地址为当前指令PC+offset
// offset为操作码后两字节(I2类型)
type GOTO struct {
	base.BranchInst
}

func (self *GOTO) Execute(frame *rtda.Frame) {
	self.SetBranchPC(frame)
}

// *****************************************************************
// 跳转通用函数
func branch(frame *rtda.Frame, offset I4) {
	pc := I8(frame.Thread().PC())
	Offset := I8(offset)
	addr := pc + Offset
	frame.SetNextPC(U4(addr))
}

// *****************************************************************
// 实现switch case语句的指令。用于case值可以编码成索引表的情况
// 从栈顶弹出一个int型变量。在low-high间匹配(闭区间),
// 匹配到则在jumpOffsets表中找offset进行跳转
// 否则用defaultOffset跳转
type TABLE_SWITCH struct {
	defaultOffset I4
	low           I4
	high          I4
	jumpOffsets   []I4
}

func (self *TABLE_SWITCH) ReadOperands(reader *base.CodeReader) {
	reader.SkipPadding()
	self.defaultOffset = reader.ReadI4()
	self.low = reader.ReadI4()
	self.high = reader.ReadI4()
	length := self.high - self.low + 1
	self.jumpOffsets = make([]I4, length)
	for i := 0; i < int(length); i++ {
		self.jumpOffsets[i] = reader.ReadI4()
	}
}
func (self *TABLE_SWITCH) Execute(frame *rtda.Frame) {
	choose := frame.OPStack().PopInt()
	offset := self.defaultOffset
	if choose >= self.low && choose <= self.high {
		offset = self.jumpOffsets[choose-self.low]
	}
	branch(frame, offset)
}

// *****************************************************************
// 实现switch case语句的指令。用于case值不能编码成索引表的情况
// 从栈顶弹出一个int型变量。在matchOffsets表中匹配
// matchOffsets表计数索引为key值，其后面紧邻的元素为对应的offset
// 匹配到则在用该offset进行跳转
// 否则用defaultOffset跳转
type LOOKUP_SWITCH struct {
	defaultOffset I4
	npairs        I4
	matchOffsets  []I4
}

func (self *LOOKUP_SWITCH) ReadOperands(reader *base.CodeReader) {
	reader.SkipPadding()
	self.defaultOffset = reader.ReadI4()
	self.npairs = reader.ReadI4()
	self.matchOffsets = make([]I4, self.npairs*2)
	for i := 0; i < int(self.npairs*2); i++ {
		self.matchOffsets[i] = reader.ReadI4()
	}
}
func (self *LOOKUP_SWITCH) Execute(frame *rtda.Frame) {
	choose := frame.OPStack().PopInt()
	offset := self.defaultOffset
	for i := 0; i < int(self.npairs*2)-1; i += 2 {
		if self.matchOffsets[i] == choose {
			offset = self.matchOffsets[i+1]
			break
		}
	}
	branch(frame, offset)
}
